//{==============================================================================================================
//! @file    ASM_N.cpp
//! @date    2014-11-02 22:01
//! @author  Vladimir Gurinovich <whistleVG@gmail.com>
//!
//!
//!
//! @par    The program interprets the input from one file to code in another file,
//!         using the file commands.h to associate the command and its number.
//!
//!         (with text labels)
//}==============================================================================================================

//!--------------------------------------------------------------------------------------------------------------
//! Including libraries
//!--------------------------------------------------------------------------------------------------------------
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

enum  CPU_Commands_t
{
    #define DEF_CMD(name,number,code,reg) name = number,
    #include "commands.h"
    #undef DEF_CMD(name,number,code,reg)
    FINISH
};

//!--------------------------------------------------------------------------------------------------------------
//! Enum, which shows type of argument
//!--------------------------------------------------------------------------------------------------------------
enum type
{
    NUMBER      = 1,
    REGISTER    = 2,
    LABEL       = 3,
    NO_ARG      = 4
};

CPU_Commands_t command;
type type_arg;

//!--------------------------------------------------------------------------------------------------------------
const int MAX_CODE_SIZE     = 500;
const int MAX_TAGS_NUMBER   = 10;
const int MAX_COMMAND_SIZE  = 10;   //!< max size of command

const int MAX_NAME_LABEL    = 10;
const int STOP_LABEL_READ   = -1;
//!--------------------------------------------------------------------------------------------------------------
const int AX_CODE           = 0;
const int BX_CODE           = 1;
const int CX_CODE           = 2;
const int DX_CODE           = 3;
//!--------------------------------------------------------------------------------------------------------------
const int EAX_CODE          = 4;    //!< in case you want more registers
const int EBX_CODE          = 5;
const int EFX_CODE          = 6;
//!--------------------------------------------------------------------------------------------------------------

double analysis_command(char* comm_string);
int analysis_argument(int* number_c);

//!--------------------------------------------------------------------------------------------------------------
//! Struct needed to create text label
//!--------------------------------------------------------------------------------------------------------------
struct label_t
{
    int  label_number;
    char* label_name ;
};


//!--------------------------------------------------------------------------------------------------------------
//! Printing positions (which labels point) to file. (label.txt)
//!--------------------------------------------------------------------------------------------------------------
void Labels_to_file(label_t* labels);

//!--------------------------------------------------------------------------------------------------------------
//! Parses the input and populates 2 array : labels and commands
//!--------------------------------------------------------------------------------------------------------------
void Reading_input(FILE* input_f , double* asm_code , label_t* labels , int* step , int* tag_step);
void Open_and_Read(FILE* input_f , FILE* code_f , double* asm_code , label_t* labels , int* step , int* tag_step);

//!--------------------------------------------------------------------------------------------------------------
//! Function, which write register code in ASM code
//!--------------------------------------------------------------------------------------------------------------
void Work_with_Register(FILE* input_f , double* asm_code , int* step);

//!--------------------------------------------------------------------------------------------------------------
//! Function , which compliance label number with position in code
//!--------------------------------------------------------------------------------------------------------------
void Work_with_label(char* comm_string , int* tag_step , label_t* labels , int* step , FILE* input_f);

//!--------------------------------------------------------------------------------------------------------------
//! Function , which finds label number by label name
//!--------------------------------------------------------------------------------------------------------------
void Analysis_label(FILE* input_f , char* label_name_arg , double* asm_code , label_t* labels , int* step);

void Destruct_labels(label_t* labels);
//!--------------------------------------------------------------------------------------------------------------
//! Function, which fill in file with code using the array of ASM
//!--------------------------------------------------------------------------------------------------------------
void FILL_ASM(FILE* code_f , double* asm_code , int step);

//!--------------------------------------------------------------------------------------------------------------
//! Function, which print interpreted code in console
//!--------------------------------------------------------------------------------------------------------------
void Print_code(double* asm_code , int* step);

//!--------------------------------------------------------------------------------------------------------------
//! in this version CPU do not know names of regs;
//!--------------------------------------------------------------------------------------------------------------
void Reg_Compliance();

//!==============================================================================================================


void Fill_labels(label_t* labels);

int main()
{

    double* asm_code =  (double*)calloc(MAX_CODE_SIZE , sizeof(int));
    label_t* labels =       (label_t*)    calloc(MAX_TAGS_NUMBER , sizeof(label_t));

    assert(asm_code);
    assert(labels);

    Fill_labels(labels);

    int tag_step = 0;
    int step = 0;

    FILE* input_f = fopen("input.txt" , "r"); assert(input_f);
    FILE* code_f =  fopen("code.txt" , "w");  assert(code_f);

    Open_and_Read(input_f , code_f , asm_code , labels , &step , &tag_step);

    Print_code(asm_code , &step);
    FILL_ASM(code_f , asm_code , step);


    Labels_to_file(labels);

    free(asm_code); asm_code = NULL;
    Destruct_labels(labels); free(labels); labels   = NULL;

    Reg_Compliance();

    return 0;
}

//!==============================================================================================================

void Open_and_Read(FILE* input_f , FILE* code_f , double* asm_code , label_t* labels , int* step , int* tag_step)
{
    assert(input_f);
    assert(code_f);
    assert(labels);
    assert(step);

    Reading_input(input_f , asm_code , labels , step , tag_step);

    fseek (input_f , 0 , SEEK_SET);
    *step = 0;

    Reading_input(input_f , asm_code , labels , step , tag_step);

}

//!==============================================================================================================

void Print_code(double* asm_code , int* step)
{
    assert(asm_code);
    assert(step);

    printf("This is asm code :\n");
    for(int i = 0; i < *step; i++)
    {
        printf("%lg " , asm_code[i]);
    }
}

//!==============================================================================================================

double analysis_command(char* comm_string)
{
    assert(comm_string);

    #define DEF_CMD(name , number ,  code , reg) if(strcmp(#name , comm_string) == 0) return number;
    #include "commands.h"
    #undef DEF_CMD(name , number , code , reg)
}

int analysis_argument(double* number_c)
{
    assert(number_c);

    switch((int)(*number_c))
    {

    #define DEF_CMD(name , number , code , reg) case number : return reg; break;
    #include "commands.h"
    #undef DEF_CMD(name , number , code , reg)

    }
}

double take_valarg(FILE* input_f)
{

    assert(input_f);

    double argument = 0;

    int OK = fscanf(input_f , "%lg" , &argument);
    assert(OK);

    return argument;

}

//!==============================================================================================================

void Reading_input(FILE* input_f , double* asm_code , label_t* labels , int* step , int* tag_step)
{
    assert(input_f);
    assert(asm_code);
    assert(labels);
    assert(step);

    char comm_string[MAX_COMMAND_SIZE] = {};
    char label_name_arg[MAX_NAME_LABEL] = {};

    while(analysis_command(comm_string) != EXIT)
    {
        int OK = fscanf(input_f , "%s" , comm_string);
        assert(OK);

        if(comm_string[0] != ':')
        {
            double number_c = analysis_command(comm_string);
            asm_code[*step] = number_c;
            (*step)++;

            int label_value = 0;

            switch(analysis_argument(&number_c))
            {
            case NUMBER:   //!<  argument - number

                asm_code[*step] = take_valarg(input_f); (*step)++;
                break;

            case REGISTER: //!<  argument - register

                Work_with_Register(input_f , asm_code , step);
                break;

            case LABEL:    //!<  argument - label
                Analysis_label(input_f , label_name_arg , asm_code , labels , step);
                break;

            case NO_ARG:   //!<  no argument
                break;

            }

        }

        else
        {

            Work_with_label(comm_string , tag_step , labels , step , input_f);

        }
    }
}

//!==============================================================================================================

void Analysis_label(FILE* input_f , char* label_name_arg , double* asm_code , label_t* labels , int* step)
{
    assert(input_f);
    assert(label_name_arg);
    assert(asm_code);
    assert(labels);
    assert(step);

    int OK = fscanf(input_f , "%s" , label_name_arg);
    assert(OK);

    for(int i = 0; i < MAX_TAGS_NUMBER; i++)
    {
        if(strcmp(label_name_arg , (labels + i)->label_name ) == 0)
        {
            asm_code[*step] = (labels + i)->label_number;
        }
    }

    (*step)++;

}

//!==============================================================================================================

void Work_with_label(char* comm_string , int* tag_step , label_t* labels , int* step  , FILE* input_f)
{
    assert(comm_string);
    assert(tag_step);
    assert(labels);
    assert(step);

    char* label_scanf_name = (char*)calloc(MAX_COMMAND_SIZE , sizeof(char));


    if(strlen(comm_string) != 1)
    {
        int OK = sscanf(comm_string + 1 , "%s" , label_scanf_name);   assert(OK);
    }
    else
    {
        int OK = fscanf(input_f , "%s" , label_scanf_name);           assert(OK);
    }


    (labels + *tag_step)->label_name    = label_scanf_name;

    (labels + *tag_step)->label_number  = *step;

    (*tag_step)++;
}

//!==============================================================================================================

void Work_with_Register(FILE* input_f , double* asm_code , int* step)
{
    assert(asm_code);
    assert(step);
    assert(input_f);

    char variable[10] = {};
    int OK = fscanf(input_f, "%s" , &variable);
    assert(OK);

    if(strcmp(variable , "ax") == 0) {asm_code[*step] = AX_CODE; (*step)++;}

    else if(strcmp(variable , "bx") == 0) {asm_code[*step] = BX_CODE; (*step)++;}

    else if(strcmp(variable , "cx") == 0) {asm_code[*step] = CX_CODE; (*step)++;}

    else if(strcmp(variable , "dx") == 0) {asm_code[*step] = DX_CODE; (*step)++;}

    else printf("ERROR WRONG REGISTER");

}

//!==============================================================================================================

void FILL_ASM(FILE* code_f , double* asm_code , int step)
{
    assert(code_f);
    assert(asm_code);

    for(int i = 0 ; i < step; i++)
    {
        fprintf(code_f , "%lg " , asm_code[i]);
    }
}

//!==============================================================================================================

void Reg_Compliance()
{
    printf("\n\nnumber ax %d\n" , AX_CODE);
    printf("number bx %d\n" , BX_CODE);
    printf("number cx %d\n" , CX_CODE);
    printf("number dx %d\n" , DX_CODE);
}

//!==============================================================================================================

void Fill_labels(label_t* labels)
{
    assert(labels);
    for(int i = 0; i < MAX_TAGS_NUMBER; i++)
    {

        (labels + i)->label_number = -1;

        (labels + i)->label_name   = (char*)calloc(MAX_NAME_LABEL , sizeof(char));

    }
}

//!==============================================================================================================

void Destruct_labels(label_t* labels)
{
    assert(labels);
    for(int i = 0; i < MAX_TAGS_NUMBER; i++)
    {

        (labels + i)->label_number = -1;

        free( (labels + i)->label_name  );

    }
}

//!==============================================================================================================

void Labels_to_file(label_t* labels)
{
    assert(labels);
    FILE* labels_f = fopen("labels.txt" , "w");  assert(labels_f);

    for(int i = 0; i < MAX_TAGS_NUMBER; i++)
    {
        if( ((labels + i)->label_number) == -1) {break;}

        fprintf(labels_f , "%d " , ((labels + i)->label_number));
    }
    fprintf(labels_f , "%d " , STOP_LABEL_READ);
}

//!==============================================================================================================

/*
void Labels_to_file(label_t* labels)
{

    FILE* labels_f = fopen("labels.txt" , "w");  assert(labels_f);


    qsort(labels , MAX_TAGS_NUMBER , sizeof(label_t) , COMPARE);

    for(int i = 0; i < MAX_TAGS_NUMBER; i++)
    {
        printf("\nLABEL_NUMBER%d\n" , (labels+i)->label_number);
    }

    for(int i = 0; i < MAX_TAGS_NUMBER; i++)
    {
        if( ((labels + i)->number) != -1)
        {
            fprintf(labels_f , "")
        }
    }


    fclose(labels_f);

}

int COMPARE(const void* a ,const void* b)
{
    assert(a);
    assert(b);

    return (((label_t*)a)->label_number) - (((label_t*)b)->label_number);
}

*/















